package de.dhbw.minf12.mas.mediator;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import de.dhbw.minf12.mas.Permutation;
import de.dhbw.minf12.mas.excel_export.DetailedExcelExporter;
import de.dhbw.minf12.mas.voter.ApprovalVoter;

/**
 * Mediator nach dem Ranked bzw. Borda Verfahren.
 *
 */
public class RankedMediator extends AbstractMediator {

	private List<ApprovalVoter> agentList;

	public RankedMediator(List<ApprovalVoter> agents, int numberOfContractVotes,
			int numberOfContractsInRound, DetailedExcelExporter exporter) {
		super(numberOfContractVotes, numberOfContractsInRound, exporter);
		this.agentList = agents;
		this.numberOfElementsInContract = agentList.get(0).getJobCount();
		this.numberOfAgents = agentList.size();
		this.currentContract = new Permutation(numberOfElementsInContract);
	}

	@Override
	public Permutation startMediation() {
		int i = 0;
		while(numberExecutedContracts <= numberOfContractVotes){
			
			int[] solutionValues = new int[3];
			int j = 0;
			for (ApprovalVoter agent: agentList) {
				solutionValues[j++] = agent.getSolutionValue(currentContract);
			}
			exporter.addRow(i++, solutionValues[0], solutionValues[1]);

			// Get the contracts for voting
			List<Permutation> newContracts = getContracts();

			// Prepare for voting
			List<List<Permutation>> acceptedContractsList = new ArrayList<List<Permutation>>();

			// Let all agents vote
			for (ApprovalVoter agent: agentList) {
				acceptedContractsList.add(
						agent.vote(newContracts, currentContract, numberOfContractsInRound));
			}
			
			int ranking = Integer.MAX_VALUE;
			Permutation acceptedNewContract = null;
			for(Permutation newCandidate : newContracts){
				int newRanking = this.getContractRanking(newCandidate, acceptedContractsList);
				if(newRanking < ranking){
					ranking = newRanking;
					acceptedNewContract = newCandidate;
				}
			}
			
			boolean accepted = true;
			for (ApprovalVoter agent: agentList) {
				if(!agent.vote(acceptedNewContract, currentContract)){
					accepted = false;
				}
			}
			
			Random rnd = new Random();
			if(accepted || rnd.nextInt(numberOfContractsInRound) < this.getMinApprovalCount()){
				currentContract = acceptedNewContract;
			}

			numberExecutedContracts += numberOfContractsInRound;	
		}
		
		return currentContract;
	}
	
	
	/**
	 * Berechnet das Ranking eines Vertrages, indem es die Platzierung in den Agent-Vote-Listen addiert.
	 * Niedrigere Werte sind besser als höhere.
	 * ACHTUNG: pPermutation muss in allen Contract Listen vorhanden sein
	 * @param pPermutation
	 * @param pAcceptedContractsList
	 * @return Ranking
	 */
	private int getContractRanking(Permutation pPermutation, List<List<Permutation>> pAcceptedContractsList){
		int ranking = 0;
		
		for(List<Permutation> contractList : pAcceptedContractsList){
			ranking += contractList.indexOf(pPermutation);
		}
		
		return ranking;
	}

	private List<Permutation> getContracts() {
		List<Permutation> newContracts = 
				currentContract.getNeighbors(numberOfContractsInRound - 1);
		newContracts.add(currentContract);
		return newContracts;
	}

	/**
	 * Berechnet den minApprovalCount. Wird in 10 Stufen geteilt und von
	 * 50% bis 0% der Verträge pro Runde berechnet.
	 * @return
	 */
	private int getMinApprovalCount() {
		int minApprovalCount = 0;
		if((int)(numberOfContractVotes * 0.1) > numberExecutedContracts){
			minApprovalCount = (int)(numberOfContractsInRound * 0.5);
		}else if((int)(numberOfContractVotes * 0.2) > numberExecutedContracts){
			minApprovalCount = (int)(numberOfContractsInRound * 0.45);
		}else if((int)(numberOfContractVotes * 0.3) > numberExecutedContracts){
			minApprovalCount = (int)(numberOfContractsInRound * 0.4);
		}else if((int)(numberOfContractVotes * 0.4) > numberExecutedContracts){
			minApprovalCount = (int)(numberOfContractsInRound * 0.3);
		}else if((int)(numberOfContractVotes * 0.5) > numberExecutedContracts){
			minApprovalCount = (int)(numberOfContractsInRound * 0.25);
		}else if((int)(numberOfContractVotes * 0.6) > numberExecutedContracts){
			minApprovalCount = (int)(numberOfContractsInRound * 0.2);
		}else if((int)(numberOfContractVotes * 0.7) > numberExecutedContracts){
			minApprovalCount = (int)(numberOfContractsInRound * 0.1);
		}else if((int)(numberOfContractVotes * 0.8) > numberExecutedContracts){
			minApprovalCount = (int)(numberOfContractsInRound * 0.05);
		}else if((int)(numberOfContractVotes * 0.9) > numberExecutedContracts){
			minApprovalCount = (int)(numberOfContractsInRound * 0.03);
		}else{
			minApprovalCount = (int)(numberOfContractsInRound * 0);
		}
		return minApprovalCount;
	}

}
